<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
  <script src="https://www.jsviews.com/download/jsviews.min.js"></script>
  <link href="https://www.jsviews.com/samples/samples.css" rel="stylesheet" />
</head>
<body>

  <div class="left">
    <button id="changeObjects">Change data</button><br/>
    <button id="insert">Add phone</button>
    <div id="result"></div>
  </div>
  
  <div class="logBox">
    <label><input type="checkbox" checked id="attach"/> Change Log</label>
    <button class="clear">Clear</button>
    <div class="messages"></div>
  </div>
  
  <script id="personTmpl" type="text/x-jsrender">
    <table class="nowidth"><tbody>
      <tr><td>Name:</td><td><input data-link="name" /></td></tr>
      <tr><td>Street:</td><td><input data-link="address^street" /></td></tr>
      <tr><td>Phones:</td><td>
        <table class="nowidth"><tbody>
          {^{for phones}}
            <tr><td>
              <input class="floatleft" data-link="number" />
              <span class="remove"></span>
            </td></tr>
          {{/for}}
        </tbody></table>
      </td></tr>
    </tbody></table>
  </script>
  
  <script>
  // Compiled template
  var tmpl = $.templates("#personTmpl");
  
  // Data: hierarchy of plain objects and arrays
  var person = {
    name: "Pete",
    address: {
      street: "1st Ave"
    },
    phones: [{number: "111 111 1111"}, {number:"222 222 2222"}] 
  };
  
  // Render and link template against plain object hierarchy
  tmpl.link("#result", person);
  
  // Button event handlers for changes
  $("#changeObjects").on("click", function() {
    $.observable(person).setProperty({
      name: "newName",
      address: {street: "New Street"},
      phones: [{number: "123 123 1234"}, {number: "321 321 4321"}]
    });
  });
  
  $("#insert").on("click", function() {
    $.observable(person.phones).insert({
      number: "456 456 4567"
    });
  });
  
  $("#result").on("click", ".remove", function() {
    $.observable(person.phones).remove(
      $.view(this).index
    )
  });
  
  // Change log code
  $(".clear").on("click", function() {
    $(".messages").empty();
  });
  
  $("#attach").on("click", function(x) {
    logChanges(this.checked);
  });
  
  logChanges(true);
  
  function logChanges(enable) {
    if (enable) {
      $.observable(person).observeAll(changeHandler);
    } else {
      $.observable(person).unobserveAll(changeHandler);
    }
  }
  
  function changeHandler(ev, eventArgs) {
    var message = "";
    if (ev.data.observeAll) {
      message += "<div><em>observeAll path:</em> " + ev.data.observeAll.path() + "</div>"
    }
    for (var key in eventArgs) {
      message += "<div><em>" + key + ":</em> "
        + $.views.converters.encode(JSON.stringify(eventArgs[key])) + "</div>";
        // (Note that we encode < > and & as HTML entities, for display)
    }
    $(".messages").append("<div>" + message + "</div>");
  }
  </script>
  
  </body>
</html>